/* Copyright (c) 2006, 2009, Carl Burch. License information is located in the
 * com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */
 
package com.cburch.draw.model;

import com.cburch.draw.canvas.CanvasObject;
import com.cburch.logisim.data.Attribute;
import com.cburch.logisim.data.AttributeEvent;
import com.cburch.logisim.data.AttributeListener;
import com.cburch.logisim.data.AttributeSet;
import com.cburch.logisim.collections.EventSourceWeakSupport;

import java.util.Iterator;
import java.util.List;

/**
 * Represents a drawing member. Classes that extend <code>DrawingMember</code> include
 * <code>Line</code>, <code>Poly</code>, and <code>Rectangular</code>.
 */
public abstract class DrawingMember implements AttributeSet, CanvasObject, Cloneable {
    private EventSourceWeakSupport listeners;
    
    public DrawingMember() {
        listeners = new EventSourceWeakSupport();
    }
    
    /**
     * Returns self as an <code>AttributeSet</code>.
     */
    public AttributeSet getAttributeSet() {
        return this;
    }
    
    protected abstract void setAttrValue(Attribute attr, Object value);
    public abstract void translate(int dx, int dy);
    public abstract void moveHandle(int index, int dx, int dy);
    
    /**
     * Inserting a handle is not currently supported.
     */
    public boolean canInsertHandle(int handleIndex) {
        return false;
    }
    
    /**
     * Deleting a handle is not currently supported.
     */
    public boolean canDeleteHandle(int handleIndex) {
        return false;
    }
    
    /**
     * Inserting a handle is not currently supported.
     */
    public void insertHandle(int handleIndex) {
        throw new UnsupportedOperationException("insertHandle");
    }
    
    /**
     * Deleting a handle is not currently supported.
     */
    public void deleteHandle(int handleIndex) {
        throw new UnsupportedOperationException("deleteHandle");
    }

    // methods required by AttributeSet interface
    public abstract List getAttributes();
    public abstract Object getValue(Attribute attr);

    /**
     * Add an <code>AttributeListener</code> to the listeners of the class.
     * @param l <code>AttributeListener</code> to add
     */
    public void addAttributeListener(AttributeListener l) {
        listeners.add(l);
    }

    /**
     * Remove an <code>AttributeListener</code> from the listeners of the class.
     * @param l <code>AttributeListener</code> to remove
     */
    public void removeAttributeListener(AttributeListener l) {
        listeners.remove(l);
    }
    
    /**
     * Clones the <code>DrawingMember</code> object.
     * Does not include listeners in the clone.
     */
    public Object clone() {
        try {
            DrawingMember ret = (DrawingMember) super.clone();
            ret.listeners = new EventSourceWeakSupport();
            return ret;
        } catch(CloneNotSupportedException e) {
            return null;
        }
    }

    /**
     * Determines if the <code>AttributeSet</code> contains the <code>Attribute</code>.
     * @param attr <code>Attribute</code> to test
     * @return <code>boolean</code> indicating if the <code>AttributeSet</code> contains the <code>Attribute</code>
     */
    public boolean containsAttribute(Attribute attr) {
        return getAttributes().contains(attr);
    }
    
    /**
     * Returns an <code>Attribute</code> whose name matches the parameter.
     * @param name <code>String</code> name of the <code>Attribute</code>
     * @return <code>Attribute</code> with the name of the parameter
     */
    public Attribute getAttribute(String name) {
        for(Iterator it = getAttributes().iterator(); it.hasNext(); ) {
            Attribute attr = (Attribute) it.next();
            if(attr.getName().equals(name)) return attr;
        }
        return null;
    }

    /**
     * Returns false. <code>DrawingMember</code> classes are not read only.
     * @return <code>boolean</code> false
     */
    public boolean isReadOnly(Attribute attr) {
        return false;
    }
    
    /**
     * This operation is not supported. <code>DrawingMember</code> classes are not read only.
     */
    public void setReadOnly(Attribute attr, boolean value) {
        throw new UnsupportedOperationException("setReadOnly");
    }

    /**
     * Set the value of the <code>Attribute</code>.
     * If the <code>Attribute</code> value changes, all <code>AttributeListeners</code> will
     * be notified of the change.
     * @param attr <code>Attribute</code> to set the value of
     * @param value <code>Object</code> to set the value of the <code>Attribute</code> to
     */
    public final void setValue(Attribute attr, Object value) {
        Object old = getValue(attr);
        boolean same = old == null ? value == null : old.equals(value);
        if(!same) {
            setAttrValue(attr, value);
            AttributeEvent e = new AttributeEvent(this, attr, value);
            for(Iterator it = listeners.iterator(); it.hasNext(); ) {
                AttributeListener l = (AttributeListener) it.next();
                l.attributeValueChanged(e);
            }
        }
    }
}
